#include <windows.h>

typedef struct  {
       long       RootNode;          /*byte offset to root node*/
       long       FreeNode;          /*byte offset to next free block*/
       BYTE       Reserve1[ 4 ];
       WORD       KeyLen;            /*length of key*/
       BYTE       IndexOp;          /*bit field :   1 = unique
                                                    8 = FOR clause
                                                   32 = compact index
                                                   64 = compound index
                                                  128 = internal tag index*/
       BYTE       IndexSig;
       BYTE       Reserve3[486];
       WORD 	  AscDesc;      /*0 = ascending; 1=descending*/
       WORD 	  Reserve4;
       WORD       ForLen;       /*length of FOR clause*/
       WORD       Reserve5;
       WORD       IndExpLen;       /*length of index expression*/
       char       Pool[512];    /* Pool for key expresion and 'for' expresion*/
      } CDXHEADER, * PCDXHEADER;


     /* In .CDX files, there are two types of node : interior and exterior.
       Interior nodes contain keyed pointers to other nodes,
       exterior nodes point to actual data items in the indexed database.

       Furthermore, there may be many indexes, (using different keys) in a
       single .CDX file.  To manage this, there is a 'master' index of
       'tags' at the beginning of the file.  Different indexes are identified
       by their 'tag' - a 10 char label.  The index at the top of the file,
       is keyed on these tags, and the key entries point to the index header
       for each particular tag.  Although this master (or tag) index points
       to data within the .CDX file, it is structured as an exterior node.

       Interior nodes are quite simple, very similar to .IDX file nodes.
       The ony difference is that instead of the 4-byte file offset pointer
       following the key data immediately, there is an extra four bytes,
       and the offset pointer follows that.  We are not sure what the
       intervening four bytes are actually for, but they must be skipped!

       Exterior nodes are more complex, because they are compressed.
       The key data is split, with a run of small records from the start of
       the buffer, containing record number, duplicate count, and trailing
       byte count.  Starting at the end of the buffer, and working backwards,
       is the compressed key data.  All trailing bytes (spaces) are removed.
       The number of trailing bytes removed is stored in the associated
       record at the start of the buffer.  If any of the first characters
       in the key are the same as for the previous key, they are omitted, and
       the number is stored in the associated record at the start of the
       buffer.  So the key data starts at each end of the buffer and works
       toward the center.

       The records at the start (recno, dups, trails) are stored in as few
       bytes as possible.  This means we have to pull off values as variable
       length bit fields.  This is tricky. */

       /*information common to both node types - at the start of both of them*/

typedef struct {
       WORD NodeAtt;
       /* 0 : Index node     - interior
          1 : Root node      - interior
          2 : Leaf node      - exterior
          3 : Root-Leaf node - exterior */
       WORD NumKeys;    /* number of keys in this node*/
       long LeftPtr;   /* byte offset of previous node at this level*/
       long RightPtr;    /* byte offset of next node at this level*/
       } NODECOMM, * PNODECOMM;

     /* The fact that nodes of a particular level are 'threaded' together
       means that to do serial indexed access, we do not need to bother
       with 'tree-walking', we can simply skim along at the bottom level.*/

     /*key data for interior nodes*/
typedef struct {
       BYTE conten [500];
       } INTKDATA,* PINTKDATA;

     /*shortened key data for exterior nodes*/
typedef struct {
       BYTE conten [488];
       } EXTKDATA,* PEXTKDATA;

     /*extra data for external nodes*/
typedef struct  {
       WORD FreeSpace;     /*free space in this key*/
       long RecNumMask;    /*bit mask for record number*/
       BYTE DupCntMask;    /*bit mask for duplicate byte count*/
       BYTE TrlCntMask;    /*bit mask for trailing bytes count*/
       BYTE RecNumBits;    /*num of bits used for record number*/
       BYTE DupCntBits;    /*num of bits used for duplicate count*/
       BYTE TrlCntBits;    /*num of bits used for trail count*/
       BYTE BytesNeeded;   /*bytes needed to store recno+dups+trail*/
       } EXTEXTRA,* PEXTEXTRA;

     /*variant record for nodes - interior / exterior*/
typedef struct {
		EXTEXTRA Extra;
		EXTKDATA ExtData;
	       } EXTNODE;
typedef union {
	     INTKDATA IntData;
	     EXTNODE  ExtNode;
         } NODES, * PNODES;

typedef struct {
       NODECOMM Common;
       NODES Nodes;
       }  NODE, * PNODE;

     /*our record for handling CDX files*/
typedef struct {
       HANDLE cdxfile;         // file handle
       char      cdxTag[11];   // tag name to load when opening
       char      cdxExp[256];  // index expression, must be same as in file
       CDXHEADER cdxHeader;    // header - used for both tag index
       CDXHEADER cdxTagHeader; // Header used for actual index
       long      TagPosFile ;  // Point to position of TagHeader in file
       BOOL      ValidTag;     // True if valid tag was loaded
       BOOL      InteriorOp;   // True if the operation is for internal Tag
       char      TagName[11];  // Tagname in use
       NODE      Node;         // node record, for both interior and exterior
       long      LastIntNode;  // point to last interior node loaded
       long      LastExtNode;  // point to last exterior node loaded
       char      KeyStr[256];  // last constructed key
       long      KeyRNo;       // record number for last key item
       BYTE      DupCnt;       // Duplicates bytes for last key
       BYTE      TrlCnt;       // Trailing blanks for last key
       WORD      KeyNoInNode;  // Number of key in loaded node
     } CDXINFO, * PCDXINFO;

#define CDXKEYCHAR  0x01
#define CDXKEYNUM   0x02

long cdxadd(  PCDXINFO cdxinfo , LPSTR szkey, long RecPos, WORD KeyType );
BOOL cdxaddtag(PCDXINFO cdxinfo , char * szTag , char * szIndExp, char * szForExp );
BOOL cdxcreate( HANDLE hFile , PCDXINFO cdxinfo );
void cdxdel(PCDXINFO cdxinfo , char * szkey , WORD KeyType);
void cdxdeltag(PCDXINFO cdxinfo , char * szkey);
long cdxgetnumkeys( PCDXINFO cdxinfo );
void cdxgettagforexp(PCDXINFO cdxinfo, char* szfor);
void cdxgettagkeyexp(PCDXINFO cdxinfo, char* szkey);
long cdxgobottom(PCDXINFO cdxinfo );
long cdxgotop( PCDXINFO cdxinfo );
BOOL cdxopen( HANDLE hFile , PCDXINFO cdxinfo, char* sztagname);
long cdxseek(PCDXINFO cdxinfo , char * szkey , BOOL SoftSeek , WORD KeyType);
BOOL cdxsettagbyname(PCDXINFO cdxinfo, char* sztagname);
BOOL cdxsettagbypos(PCDXINFO cdxinfo, WORD pos);
long cdxskip(PCDXINFO cdxinfo , long lrecoffset);